package it.blueocean.acanto.entities.platform;

import it.blueocean.acanto.entities.platform.security.Authority;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Index;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * @author Fabio Mauro
 *
 */
@Entity
@Table(name = "TB_USERS")
@org.hibernate.annotations.Table(appliesTo = "TB_USERS", indexes = { @Index(name = "IDX_ENABLE", columnNames = { "ENABLED" }) })
public class DBUser implements UserDetails, Serializable
{
	private static final long serialVersionUID = 1029844164170615804L;

	@Id
	@Column(name = "USER_ID", unique = true, nullable = false, length = 20)
	private String username;

	@Column(name = "PASSWORD", length = 500)
	private String password;

	@Temporal(TemporalType.DATE)
	@Column(name = "PASSWORD_EXPIRATION_DATE")
	private Date passwordExpirationDate;

	@Column(name = "EMAIL", length = 50)
	private String email;

	@Temporal(TemporalType.DATE)
	@Column(name = "EXPIRATION_DATE")
	private Date expirationDate;

	@Column(name = "COUNTRY", length = 2)
	private String country;

	@Column(name = "LANGUAGE", length = 2)
	private String language;

	@Column(name = "HOMEPAGE")
	private String homepage;

	@Column(name = "ENABLED", columnDefinition = "VARCHAR(1) DEFAULT 'T' NOT NULL CHECK (ENABLED IN ('T','F'))")
	private boolean enabled;
	
	@Column(name = "ACCOUNT_NON_EXPIRED", columnDefinition = "VARCHAR(1) DEFAULT 'T' NOT NULL CHECK (ENABLED IN ('T','F'))")
	private boolean accountNonExpired;

	@Column(name = "ACCOUNT_NON_LOCKED", columnDefinition = "VARCHAR(1) DEFAULT 'T' NOT NULL CHECK (ENABLED IN ('T','F'))")
	private boolean accountNonLocked;

	@Column(name = "CREDENTIALS_NON_EXPIRED", columnDefinition = "VARCHAR(1) DEFAULT 'T' NOT NULL CHECK (ENABLED IN ('T','F'))")
	private boolean credentialsNonExpired;

	@ElementCollection(targetClass = Group.class, fetch = FetchType.EAGER)
	@JoinTable(name = "TB_USER_GROUP", 
			joinColumns = { @JoinColumn(name = "USER_ID") }, 
			inverseJoinColumns = { @JoinColumn(name = "GROUP_ID") })
	@ForeignKey(name = "FK_TB_USER_PROFILES", inverseName = "FK_TB_GROUP_ID")
	@org.hibernate.annotations.OrderBy(clause = "GROUP_ID asc")
	private Set<Group> groups = new HashSet<Group>();

	public DBUser()
	{
	}
	
	public DBUser(String principal)
	{
		this.username = principal;
	}
	
	public String toString()
	{
		return new StringBuffer()
    	.append("username : " + username).append("\n")      
    	.append("password : " + password).append("\n") 
    	.append("passwordExpirationDate : " + passwordExpirationDate).append("\n") 
    	.append("email : " + email).append("\n") 
    	.append("expirationDate : " + expirationDate).append("\n") 
    	.append("country : " + country).append("\n") 
    	.append("language : " + language).append("\n") 
    	.append("homepage : " + homepage).append("\n") 
    	.append("groups : " + groups).append("\n") 
    	.toString();
	}
	
	public boolean equals(Object obj)
	{
		if (obj instanceof DBUser)
		{
			DBUser a = (DBUser) obj;
			return 
			a.getUsername().equals(username) && 
			a.getPassword().equals(password) && 
			a.getPasswordExpirationDate().equals(passwordExpirationDate) &&
			a.getEmail().equals(email) &&
			a.getExpirationDate().equals(expirationDate) &&
			a.getCountry().equals(country) &&
			a.getLanguage().equals(language) &&
			a.getHomepage().equals(homepage) &&
			a.getGroups().size() == groups.size() &&
			a.isEnabled() == enabled;
		}
		else
			return false;        	
	}
	
	public Object clone()
	{
		DBUser clone = new DBUser();
		clone.setUsername(getUsername());
		clone.setPassword(getPassword());
		clone.setPasswordExpirationDate(getPasswordExpirationDate());
		clone.setEmail(getEmail());
		clone.setExpirationDate(getExpirationDate());
		clone.setCountry(getCountry());
		clone.setLanguage(getLanguage());
		clone.setHomepage(getHomepage());
		clone.setEnabled(isEnabled());

		if (groups.size() > 0)
			clone.setGroups(groups);

		return clone;
	}
	
	/**
	 * 
	 * return a set of groups id
	 */
	public List<String> getAssociatedGroups()
	{
		List<String> result = new ArrayList<String>();

		for (Group sg : groups)
			result.add(sg.getId());
		
		return result;
	}

	public boolean isInGroup(Group groupName)
	{
		return groups.contains(groupName);
	}
	
	public void addGroup(Group group)
	{
		if (group == null) throw new IllegalArgumentException("Null staticGroup!");
        groups.add(group);
	}
    
	public void removeGroup(Group group)
	{
		 if (group == null) throw new IllegalArgumentException("Null staticGroup!");
		 groups.remove(group);
	}

	public String getEmail()
	{
		return email;
	}

	public void setEmail(String email)
	{
		this.email = email;
	}

	public Date getExpirationDate()
	{
		return expirationDate;
	}

	public void setExpirationDate(Date expirationDate)
	{
		this.expirationDate = expirationDate;
	}

	public String getPassword()
	{
		return password;
	}

	public void setPassword(String password)
	{
		this.password = password;
	}

	public Locale getLocale()
	{
		return new Locale(this.language, this.country);
	}

	public void setLocale(Locale locale)
	{
		this.language = locale.getLanguage();
		this.country = locale.getCountry();
	}

	public String getCountry()
	{
		return country;
	}

	public void setCountry(String country)
	{
		this.country = country;
	}

	public String getHomepage()
	{
		return this.homepage;
	}

	public void setHomepage(String homepage)
	{
		this.homepage = homepage;
	}

	public String getLanguage()
	{
		return language;
	}

	public void setLanguage(String language)
	{
		this.language = language;
	}

	public Date getPasswordExpirationDate()
	{
		return passwordExpirationDate;
	}

	public void setPasswordExpirationDate(Date passwordExpirationDate)
	{
		this.passwordExpirationDate = passwordExpirationDate;
	}

	public void setEnabled(boolean enabled)
	{
		this.enabled = enabled;
	}

	public void setGroups(Set<Group> groups)
	{
		this.groups = groups;
	}

	@Override
	public Collection<GrantedAuthority> getAuthorities()
	{
		Collection<GrantedAuthority> roles = new HashSet<GrantedAuthority>();
		
		for (Group group : groups)
		{
			if(group.getRoles() != null )
			{
				for (Authority authority : group.getRoles())
				{
					roles.add(new GrantedAuthorityImpl(authority.getAuthority()));
				}
			}
		}
		
		return roles;
	}

	@Override
	public String getUsername()
	{
		return this.username;
	}
	
	public void setUsername(String username)
	{
		this.username = username;
	}

	@Override
	public boolean isAccountNonExpired()
	{
		return this.accountNonExpired;
	}

	@Override
	public boolean isAccountNonLocked()
	{
		return this.accountNonLocked;
	}

	@Override
	public boolean isCredentialsNonExpired()
	{
		return credentialsNonExpired;
	}

	@Override
	public boolean isEnabled()
	{
		return this.enabled;
	}

	public void setAccountNonExpired(boolean accountNonExpired)
	{
		this.accountNonExpired = accountNonExpired;
	}

	public void setAccountNonLocked(boolean accountNonLocked)
	{
		this.accountNonLocked = accountNonLocked;
	}

	public void setCredentialsNonExpired(boolean credentialsNonExpired)
	{
		this.credentialsNonExpired = credentialsNonExpired;
	}

	public Set<Group> getGroups()
	{
		return groups;
	}

}
